/*
 * Copyright 2016-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.mongodb.core.aggregation;

import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
import org.springframework.util.Assert;

import static org.springframework.data.mongodb.core.aggregation.Fields.field;

public class LookupJoinOperation implements FieldsExposingAggregationOperation, InheritsFieldsAggregationOperation {

    private final Field from;
    private final Document let;
    private final AggregationPipeline pipeline;
    private final ExposedField as;

    /**
     * Creates a new {@link LookupJoinOperation} for the given {@link Field}s.
     *
     * @param from     must not be {@literal null}.
     * @param let      must not be {@literal null}.
     * @param pipeline must not be {@literal null}.
     * @param as       must not be {@literal null}.
     */
    public LookupJoinOperation(String from, Document let, AggregationPipeline pipeline, String as) {

        Assert.notNull(from, "From must not be null!");
        Assert.notNull(let, "Let must not be null!");
        Assert.notNull(pipeline, "Pipeline must not be null!");
        Assert.notNull(as, "As must not be null!");

        this.from = field(from);
        this.let = let;
        this.pipeline = pipeline;
        this.as = new ExposedField(field(as), true);
    }

    @Override
    public Document toDocument(AggregationOperationContext context) {

        Document lookupObject = new Document();

        lookupObject.append("from", from.getTarget());
        lookupObject.append("let", let);
        lookupObject.append("pipeline", pipeline.toDocuments(Aggregation.DEFAULT_CONTEXT));
        lookupObject.append("as", as.getTarget());

        return new Document(getOperator(), lookupObject);
    }

    @Override
    public String getOperator() {
        return "$lookup";
    }

    @Override
    public ExposedFields getFields() {
        return ExposedFields.from(as);
    }

}
